#include <math.h>
#include "bola_phys.h"
#include "chipmunk/chipmunk.h"

#define BALL_RADIUS 10.0

static cpSpace *space;

static cpBody *ball[9];

/**
 * Inicializa os elementos físicos do jogo
 */
void phys_init()
{
    cpInitChipmunk();

    space = cpSpaceNew();
    space->gravity = cpv(0.0f, 0.0f);

    /* inicializa as bolas */
    float px[9] =
        { -180.0, -180.0, -20.0, -20.0, 380.0, 380.0, 220.0, 220.0, 220.0 };
    float py[9] =
        { 100.0, -100.0, 180.0, -180.0, 100.0, -100.0, 180.0, -180.0, 0.0 };
    int i;

    for (i = 0; i < 9; i++)
    {
        ball[i] = cpBodyNew(10.0, 10.0);
        ball[i]->p = cpv(px[i], py[i]);
        cpSpaceAddBody(space, ball[i]);

        cpShape *bShape =
            cpCircleShapeNew(ball[i], BALL_RADIUS, cpv(0.0f, 0.0f));
        bShape->e = 0.7;
        bShape->u = 100.0;
        cpSpaceAddShape(space, bShape);
    }

    /* inicializa as laterais da mesa */
    cpShape *seg;

    cpBody *staticBody = cpBodyNew(INFINITY, INFINITY);

    seg =
        cpSegmentShapeNew(staticBody, cpv(400.0, -200.0), cpv(400.0, 200.0),
                          0.0);
    seg->e = 0.7;
    seg->u = 100.0;
    cpSpaceAddStaticShape(space, seg);
    seg =
        cpSegmentShapeNew(staticBody, cpv(400.0, 200.0), cpv(-200.0, 200.0),
                          0.0);
    seg->e = 0.7;
    seg->u = 100.0;
    cpSpaceAddStaticShape(space, seg);
    seg =
        cpSegmentShapeNew(staticBody, cpv(-200.0, 200.0), cpv(-200.0, -200.0),
                          0.0);
    seg->e = 0.7;
    seg->u = 100.0;
    cpSpaceAddStaticShape(space, seg);
    seg =
        cpSegmentShapeNew(staticBody, cpv(-200.0, -200.0), cpv(400.0, -200.0),
                          0.0);
    seg->e = 0.7;
    seg->u = 100.0;
    cpSpaceAddStaticShape(space, seg);
}

/**
 * Atualiza a física do jogo
 */
void phys_update()
{
    cpFloat dt = 1.0 / 60.0;

    int i;

    for (i = 0; i < 9; i++)
    {
        /* atrito */
        ball[i]->v = cpvmult(ball[i]->v, 0.995);
        if (cpvlength(ball[i]->v) < 50.0)
            ball[i]->v = cpvmult(ball[i]->v, 0.995);
        if (cpvlength(ball[i]->v) < 25.0)
            ball[i]->v = cpvmult(ball[i]->v, 0.990);
        if (cpvlength(ball[i]->v) < 5.0)
            ball[i]->v = cpvzero;
    }

    cpSpaceStep(space, dt);
}

/**
 * Aplica um impulso à bola identificada por 'num'.
 */
void ball_apply_impulse(float i, float ang, int num)
{
    cpBodyApplyImpulse(ball[num], cpvmult(cpv(cos(ang), sin(ang)), i),
                       cpvzero);
}

/**
 * Recupera a posição da bola identificada por 'num'.
 */
void ball_get_pos(float *x, float *y, int num)
{
    *x = ball[num]->p.x;
    *y = ball[num]->p.y;
}

/**
 * Seta a posição da bola identificada por 'num'.
 */
void ball_set_pos(float x, float y, int num)
{
    ball[num]->p.x = x;
    ball[num]->p.y = y;
}

/**
 * Retorna o somatório dos módulos das velocidades das bolas
 */
float balls_movement()
{
    float result = 0;

    int i;

    for (i = 0; i < 9; i++)
    {
        result += cpvlength(ball[i]->v);
    }

    return result;
}

/**
 * Checa se a bola foi encaçapada
 */
int ball_destroyed(int i)
{
    return (cpvlength(cpvsub(ball[i]->p, cpv(-200.0, -200.0))) <= 20.0) ||
        (cpvlength(cpvsub(ball[i]->p, cpv(100.0, -200.0))) <= 20.0) ||
        (cpvlength(cpvsub(ball[i]->p, cpv(400.0, -200.0))) <= 20.0) ||
        (cpvlength(cpvsub(ball[i]->p, cpv(400.0, 200.0))) <= 20.0) ||
        (cpvlength(cpvsub(ball[i]->p, cpv(100.0, 200.0))) <= 20.0) ||
        (cpvlength(cpvsub(ball[i]->p, cpv(-200.0, 200.0))) <= 20.0);
}

void ball_stop(int num)
{
    ball[num]->v = cpvzero;
}
